package com.thghh.table;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class BeanColumnAnnotationProcessor {

    protected static final Object[] EMPTY_ARGS = null;
    protected Class<?> beanClass;
    protected Map<Integer, BeanColumn> columnMap;
    protected Map<Integer, PropertyDescriptor> propertyMap;

    public BeanColumnAnnotationProcessor(Class<?> beanClass) {
        this.beanClass = beanClass;
        processor();
    }

    /**
     * Parse the bean and initialize the properties required by
     * {@link BeanColumn}
     *
     * @param clazz
     */
    public void processor() {
        columnMap = new HashMap<>();
        propertyMap = new HashMap<>();

        if (beanClass == null) {
            throw new CheckException("class is null");
        }

        try {
            Field[] fields = beanClass.getDeclaredFields();
            BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (Field field : fields) {
                // A comment of the specified type exists
                if (field.isAnnotationPresent(BeanColumn.class)) {
                    BeanColumn beanColumn = field.getAnnotation(BeanColumn.class);
                    // title name and column index
                    int index = beanColumn.index();
                    columnMap.put(index, beanColumn);

                    for (int i = 0; i < propertyDescriptors.length; i++) {
                        String propertyName = propertyDescriptors[i].getName();
                        if (field.getName().equalsIgnoreCase(propertyName)) {
                            propertyMap.put(index, propertyDescriptors[i]);
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new CheckException(e);
        }
    }

    /**
     * Get {@link BeanColumn} by index
     * 
     * @param index
     * @return
     * @author Zhikang.Peng
     * @date 2021-06-16 09:25:36
     * @since 1.0
     */
    public BeanColumn getBeanColumn(int index) {
        return columnMap.get(index);
    }

    /**
     * Get {@link PropertyDescriptor} by index
     * 
     * @param index
     * @return
     * @author Zhikang.Peng
     * @date 2021-06-16 09:26:21
     * @since 1.0
     */
    public PropertyDescriptor getPropertyDescriptor(int index) {
        return propertyMap.get(index);
    }

    /**
     * Get column count
     * 
     * @return
     * @author Zhikang.Peng
     * @date 2021-06-16 09:26:31
     * @since 1.0
     */
    public int getColumnCount() {
        return columnMap.size();
    }

    /**
     * Set object value
     * 
     * @param obj object
     * @param index property index
     * @param value value
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @author Zhikang.Peng
     * @date 2021-06-16 09:26:49
     * @since 1.0
     */
    public void setPropertyValue(Object obj, int index, Object value)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        PropertyDescriptor descriptor = propertyMap.get(index);
        if (descriptor == null) {
            return;
        }
        descriptor.getWriteMethod().invoke(obj, value);
    }

    /**
     * Get object value
     * 
     * @param obj object
     * @param index property index
     * @return
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @author Zhikang.Peng
     * @date 2021-06-16 09:27:33
     * @since 1.0
     */
    public Object getPropertyValue(Object obj, int index)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        PropertyDescriptor descriptor = propertyMap.get(index);
        if (descriptor == null) {
            return null;
        }
        return descriptor.getReadMethod().invoke(obj, EMPTY_ARGS);
    }
    
    /**
     * Get object property class type
     * @param index
     * @return
     * @author  Zhikang.Peng
     * @date 2021-05-14 11:03:05
     * @since   1.0
     */
    public Class<?> getType(Integer index) {
        PropertyDescriptor descriptor = propertyMap.get(index);
        return descriptor.getPropertyType();
    }
}
